﻿//=============================================================================
// Библиотека 'WPFExcelReport' предназначена для связи и передачи данных c PLC
//=============================================================================

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Diagnostics;

namespace WPFExcelReport.CL
{
    #region Aux class 

    /// <summary>
    /// Class descriptor of the received data
    /// </summary>
    public class ReadDataEventArgs : EventArgs
    {
        /// <summary>
        /// Accepted variables
        /// </summary>
        public List<VarData> Vars { get; private set; }

        /// <summary>
        /// Constructor ReadDataEventArgs
        /// </summary>
        internal ReadDataEventArgs(List<ItemValue> items)
        {
            Vars = new List<VarData>(items.Count());

            foreach (ItemValue iVal in items)
            {
                    VarData var = new VarData();

                    var.Addr = iVal.Addr;
                    var.Name = iVal.Name;
                    var.Value = iVal.Value;
                    var.TypeRW = TypeRW.read;

                    Vars.Add(var);
            }
        }

    }

    /// <summary>
    /// Class descriptor of the received data at xml
    /// </summary>
    public class ReadDataXmlEventArgs : EventArgs
    {
        /// <summary>
        /// Xml object data
        /// </summary>
        public XDocument XDoc { get; private set; }

        /// <summary>
        /// Constructor ReadDataXmlEventArgs
        /// </summary>
        internal ReadDataXmlEventArgs(List<ItemValue> items)
        {
            XDoc = new XDocument(new XElement("Items"));

            foreach (ItemValue iVal in items)
            {
                XDoc.Element("Items").Add(new XElement("Item",
                                          new XElement("Name", iVal.Name),
                                          new XElement("Addr", iVal.Addr),
                                          new XElement("Value", iVal.Value),
                                          new XElement("TypeRW", "read")));

            }
        }
    }

    #endregion

    /// <summary>
    /// Client S7
    /// </summary>
    public class Client
    {

        #region Constants


        #endregion

        #region Fields

        #region General structure

        /// <summary>
        /// Deskr-s for reading variables: key - the name of the variable
        /// </summary>
        private Dictionary<string, HandlItemRead> _hItemRead = new Dictionary<string, HandlItemRead>(100);

        /// <summary>
        /// Deskr-s variables for the record: key - the name of the variable
        /// </summary>
        private Dictionary<string, HandlItemWrite> _hItemWrite = new Dictionary<string, HandlItemWrite>(100);

        /// <summary>
        /// The values of variables to read: key - the name of the variable
        /// </summary>
        private Dictionary<string, object> _iValueRead = new Dictionary<string, object>(100);

        /// <summary>
        /// Addresses of variables to read: key - the name of the variable
        /// </summary>
        private Dictionary<string, string> _iAddrRead = new Dictionary<string, string>(100);

        /// <summary>
        /// Addresses of variables for the record: key - the name of the variable
        /// </summary>
        private Dictionary<string, string> _iAddrWrite = new Dictionary<string, string>(100);

        /// <summary>
        /// Server S7
        /// </summary>
        private S7.S7 _server = new S7.S7();
    
        #endregion
                

        /// <summary>
        /// Init data
        /// </summary>
        private Init _init;
                       
        /// <summary>
        /// Num error
        /// </summary>
        private int _errNum = 0;

        /// <summary>
        /// Description of errors
        /// </summary>
        private string _errTxt = "";
                      
        /// <summary>
        /// Data transmission is performed
        /// </summary>
        private bool _isConnect = false;
                     
        #endregion

        #region Properties

        /// <summary>
        /// Num error
        /// </summary>
        public int ErrNum
        {
            get
            {
                return _errNum;
            }
        }

        /// <summary>
        /// Description of error
        /// </summary>
        public string ErrTxt
        {
            get
            {
                return _errTxt;
            }
        }
                    
        #endregion

        #region Constructor

        /// <summary>
        /// Constructor Client
        /// </summary>
        public Client()
        {
            _init = new Init(_hItemRead, _hItemWrite, _iValueRead,
                             _iAddrRead, _iAddrWrite, _server);
        }
       
        #endregion

        #region Internal methods

        /// <summary>
        /// Parse error code
        /// </summary>
        /// <param name="errCode"> erroe code</param>
        /// <param name="name"> name item</param>
        private void ParseErrCode(ErrCode errCode, string name = "")
        {
            switch (errCode)
            {
                case ErrCode.Ok:
                    _errNum = (int)ErrCode.Ok;
                    _errTxt = "ОК";
                    break;

                case ErrCode.ConnFlt:
                    _errNum = (int)ErrCode.ConnFlt;
                    _errTxt = "Transmission error. Check the address and PLC";
                    break;

                case ErrCode.PingNo:
                    _errNum = (int)ErrCode.PingNo;
                    _errTxt = "Communication error, no ping. Check your network connection and the PLC address";
                    break;
                                                                                 
                case ErrCode.NameNotFound:
                    _errNum = (int)ErrCode.NameNotFound;
                    _errTxt = "Variable " + name + " does not exist";
                    break;

                case ErrCode.VarTypeOther:
                    _errNum = (int)ErrCode.VarTypeOther;
                    _errTxt = "Variable " + name + " has a different type of";
                    break;
                                 
            }
        }

        /// <summary>
        /// There was a change of variables on the server
        /// </summary>
        private void DataReadChange(object sender, WPFExcelReport.ReadDataEventArgs e)
        {
            foreach (ItemValue val in e.Vars) _iValueRead[val.Name] = val.Value;
            
            // возбуждаем события
            if (DataChange != null) DataChange(null, new ReadDataEventArgs(e.Vars));

            if (DataChangeToXml != null) DataChangeToXml(null, new ReadDataXmlEventArgs(e.Vars));
        }
             
        #endregion

        #region External methods

        /// <summary>
        /// Initialization data
        /// </summary>
        /// <param name="pathFile"> path to the file</param>
        /// <param name="typeFile"> type of file: 1 - file xlsx
        ///                                       2 - file txt 
        ///                                       3 - file xml</param>
        /// <returns> error read</returns>
        public int Init(string pathFile, TypeInitFile typeFile)
        {
            // for a new init need off the server 
            if (_isConnect) return (int)ErrCode.Ok;
                        
            ErrCode err = _init.ParseData(pathFile, typeFile); 

            if (err != ErrCode.Ok)
            {
                _errNum = _init.ErrNum;
                _errTxt = _init.ErrTxt;
                return (int)err;
            }

            ParseErrCode(ErrCode.Ok);   
            return (int)ErrCode.Ok;
        }

        /// <summary>
        /// Connect to PLC S7
        /// </summary>
        /// <param name="ipAddr"> IP addr </param>
        /// <param name="rack"> rack </param>
        /// <param name="slot"> slot </param>
        /// <param name="cpu"> type CPU</param>
        /// <param name="updInterval"> update interval data, ms</param>
        /// <returns> err code</returns>
        public int Connect(string ipAddr, int rack, int slot, CpuType cpu, int updInterval)
        {
            if (_isConnect) return (int)ErrCode.Ok;

            // описание plc
            S7.PlcDesc plc = new S7.PlcDesc();
            plc.IP = ipAddr;
            plc.Rack = rack;
            plc.Slot = slot;
            plc.Cpu = (S7.CpuType)cpu;
            plc.TimeUpd = updInterval;

            // соединение с plc
            int errNum = _server.Connect(plc);
                      
            if (errNum != 0)
            {
                ParseErrCode((ErrCode)errNum);
                return errNum;
            }
        
            // подпись на события изменения данных
            _server.DataChange += new EventHandler<WPFExcelReport.ReadDataEventArgs>(DataReadChange);

            _isConnect = true;
            
            ParseErrCode(ErrCode.Ok);
            return errNum;
        }

        /// <summary>
        /// Disconnect at PLC S7
        /// </summary>
        public void Disconnect()
        {
            if (_isConnect)
            {
                _server.Disconnect();
                _server.DataChange -= new EventHandler<WPFExcelReport.ReadDataEventArgs>(DataReadChange); 
                _isConnect = false;
            }
        }

        /// <summary>
        /// State data
        /// </summary>
        /// <returns> object Status</returns>
        public Status GetStatus()
        {
            Status st = new Status();

            st.ConnState = _server.GetConnState();
            st.ErrTxt = _server.ErrTxt;
            st.ErrNum = _server.ErrNum;

            return st;
        }

        /// <summary>
        /// Return object Items
        /// </summary>
        /// <returns> object Items for data processing</returns>
        public Items GetItems()
        {
            return new Items(_hItemRead, _hItemWrite, _iValueRead,
                             _iAddrRead, _iAddrWrite, _server);
        }

        /// <summary>
        /// Reading the value of the type Byte
        /// </summary>
        /// <param name="err"> err read</param>
        /// <param name="name"> name variable</param>
        /// <returns> value variable</returns>
        public byte ReadByte(string name, out bool err)
        {
            try
            {
                err = false;
                return (byte)_iValueRead[name];
            }
            catch (KeyNotFoundException ex)
            {
                err = true;
                ParseErrCode(ErrCode.NameNotFound, name);
                return 0;
            }
            catch (InvalidCastException ex)
            {
                err = true;
                ParseErrCode(ErrCode.VarTypeOther, name);
                return 0;
            }
        }

        /// <summary>
        /// Reading the value of the type int16
        /// </summary>
        /// <param name="err"> err read</param>
        /// <param name="name"> name variable</param>
        /// <returns> value variable</returns>
        public int ReadInt16(string name, out bool err)
        {
            try
            {
                err = false;
                return Convert.ToInt32(_iValueRead[name]);
            }
            catch (KeyNotFoundException ex)
            {
                err = true;
                ParseErrCode(ErrCode.NameNotFound, name);
                return 0;
            }
            catch (InvalidCastException ex)
            {
                err = true;
                ParseErrCode(ErrCode.VarTypeOther, name);
                return 0;
            }
        }

        /// <summary>
        /// Reading the value of the type int32
        /// </summary>
        /// <param name="err"> err read</param>
        /// <param name="name"> name variable</param>
        /// <returns> value variable</returns>
        public int ReadInt32(string name, out bool err)
        {
            try
            {
                err = false;
                return Convert.ToInt32(_iValueRead[name]);
            }
            catch (KeyNotFoundException ex)
            {
                err = true;
                ParseErrCode(ErrCode.NameNotFound, name);
                return 0;
            }
            catch (InvalidCastException ex)
            {
                err = true;
                ParseErrCode(ErrCode.VarTypeOther, name);
                return 0;
            }
        }

        /// <summary>
        /// Reading the value of the type real
        /// </summary>
        /// <param name="err"> err read</param>
        /// <param name="name"> name variable</param>
        /// <returns> value variable</returns>
        public double ReadReal(string name, out bool err)
        {
            try
            {
                err = false;
                return Convert.ToDouble(_iValueRead[name]);
            }
            catch (KeyNotFoundException ex)
            {
                err = true;
                ParseErrCode(ErrCode.NameNotFound, name);
                return 0;
            }
            catch (InvalidCastException ex)
            {
                err = true;
                ParseErrCode(ErrCode.VarTypeOther, name);
                return 0;
            }
        }

        /// <summary>
        /// Reading the value of the type string
        /// </summary>
        /// <param name="err"> err read</param>
        /// <param name="name"> name variable</param>
        /// <returns> value variable</returns>
        public string ReadString(string name, out bool err)
        {
            try
            {
                err = false;
                return Convert.ToString(_iValueRead[name]);
            }
            catch (KeyNotFoundException ex)
            {
                err = true;
                ParseErrCode(ErrCode.NameNotFound, name);
                return "";
            }
            catch (InvalidCastException ex)
            {
                err = true;
                ParseErrCode(ErrCode.VarTypeOther, name);
                return "";
            }
        }

        /// <summary>
        /// Writing a value of type Byte
        /// </summary>
        /// <param name="name"> name variable</param>
        /// <param name="setValue"> set value</param>
        /// <param name="err"> err write</param>
        public void WriteByte(string name, byte setValue, out bool err)
        {
            try
            {
                HandlItemWrite item = _hItemWrite[name];
                item.Value = setValue;
                _server.WriteData(new List<HandlItemWrite>() { item }, out err);
            }
            catch (KeyNotFoundException ex)
            {
                err = true;
                ParseErrCode(ErrCode.NameNotFound, name);
                return;
            }
            err = false;
            
        }

        /// <summary>
        /// Writing a value of type Int16
        /// </summary>
        /// <param name="name"> name variable</param>
        /// <param name="setValue"> set value</param>
        /// <param name="err"> err write</param>
        public void WriteInt16(string name, Int16 setValue, out bool err)
        {
            try
            {
                HandlItemWrite item = _hItemWrite[name];
                item.Value = setValue;
                _server.WriteData(new List<HandlItemWrite>() { item }, out err);
            }
            catch (KeyNotFoundException ex)
            {
                err = true;
                ParseErrCode(ErrCode.NameNotFound, name);
                return;
            }
            err = false;
        }

        /// <summary>
        /// Writing a value of type Int32
        /// </summary>
        /// <param name="name"> name variable</param>
        /// <param name="setValue"> set value</param>
        /// <param name="err"> err write</param>
        public void WriteInt32(string name, Int32 setValue, out bool err)
        {
            try
            {
                HandlItemWrite item = _hItemWrite[name];
                item.Value = setValue;
                _server.WriteData(new List<HandlItemWrite>() { item }, out err);
            }
            catch (KeyNotFoundException ex)
            {
                err = true;
                ParseErrCode(ErrCode.NameNotFound, name);
                return;
            }
            err = false;
        }

        /// <summary>
        /// Writing a value of type Real
        /// </summary>
        /// <param name="name"> name variable</param>
        /// <param name="setValue"> set value</param>
        /// <param name="err"> err write</param>
        public void WriteReal(string name, double setValue, out bool err)
        {
            try
            {
                HandlItemWrite item = _hItemWrite[name];
                item.Value = Convert.ToSingle(setValue);
                _server.WriteData(new List<HandlItemWrite>() { item }, out err);
            }
            catch (KeyNotFoundException ex)
            {
                err = true;
                ParseErrCode(ErrCode.NameNotFound, name);
                return;
            }
            err = false;
        }

        /// <summary>
        /// Writing a value of type String
        /// </summary>
        /// <param name="name"> name variable</param>
        /// <param name="setValue"> set value</param>
        /// <param name="err"> err write</param>
        public void WriteString(string name, string setValue, out bool err)
        {
            try
            {
                HandlItemWrite item = _hItemWrite[name];
                item.Value = setValue;
                _server.WriteData(new List<HandlItemWrite>() { item }, out err);
            }
            catch (KeyNotFoundException ex)
            {
                err = true;
                ParseErrCode(ErrCode.NameNotFound, name);
                return;
            }
            err = false;
        }

      
        #endregion

        #region Events

        /// <summary>
        /// Changing the data to be read
        /// </summary>
        public event EventHandler<ReadDataEventArgs> DataChange;

        /// <summary>
        /// Changing the data to be read - return xml 
        /// </summary>
        public event EventHandler<ReadDataXmlEventArgs> DataChangeToXml;

        #endregion

      
    }
}
